package com.yuantu.judiciary.serviceimpl;

import com.alibaba.fastjson2.JSON;
import com.yuantu.judiciary.dao.*;
import com.yuantu.judiciary.exception.enums.AccountExceptionEnum;
import com.yuantu.judiciary.exception.enums.OrganizationExceptionEnum;
import com.yuantu.judiciary.exception.enums.VipExceptionEnum;
import com.yuantu.judiciary.model.domain.*;
import com.yuantu.judiciary.model.dto.SearchRecordContentDTO;
import com.yuantu.judiciary.model.enums.*;
import com.yuantu.judiciary.model.form.*;
import com.yuantu.judiciary.model.vo.*;
import com.yuantu.judiciary.service.IAgentService;
import com.yuantu.judiciary.service.IVipInfoService;
import com.yuantu.judiciary.utils.AssertUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 套餐管理
 *
 * @author syw
 * @since 2023-08-11
 **/
@Service
@RequiredArgsConstructor
public class VipInfoServiceImpl implements IVipInfoService {

    private final IVipInfoDao vipInfoDao;
    private final IAccountDao accountDao;
    private final IOrganizationDao organizationDao;
    private final IOrgVipRecordDao orgVipRecordDao;
    private final IQueryRecordDao queryRecordDao;
    private final IVipOrderDao vipOrderDao;
    private final IOrganizationSourceDao organizationSourceDao;
    private final IVipRecordAppletDao vipRecordAppletDao;
    private final IAgentService agentService;

    @Override
    public Boolean add(VipInfoForm form) {
        VipInfoDO vipInfo = vipInfoDao.findByNameAndTypeAndDeletedFalse(form.getName(), form.getType());
        AssertUtil.assertTrue(ObjectUtils.isEmpty(vipInfo), VipExceptionEnum.VIP_NAME_EXIST);
        vipInfo = new VipInfoDO();
        form.setId(null);
        BeanUtils.copyProperties(form, vipInfo);
        vipInfoDao.save(vipInfo);
        return true;
    }

    @Override
    public Boolean modify(VipInfoForm form) {
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(form.getId()), VipExceptionEnum.VIP_NOT_EXIST);
        VipInfoDO originData = vipInfoDao.findByIdAndDeletedFalse(form.getId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(originData), VipExceptionEnum.VIP_NOT_EXIST);

        VipInfoDO vipInfo = vipInfoDao.findByIdAndNameAndTypeAndDeletedFalse(form.getId(), form.getName(), form.getType());
        boolean verify = ObjectUtils.isEmpty(vipInfo) || vipInfo.getId().equals(form.getId());
        AssertUtil.assertTrue(verify, VipExceptionEnum.VIP_NAME_EXIST);

        BeanUtils.copyProperties(form, originData);
        vipInfoDao.save(originData);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(Long accountId, List<Long> id) {
        vipInfoDao.deleteByIdIn(id, accountId, new Timestamp(System.currentTimeMillis()));
        return true;
    }

    @Override
    public List<VipInfoForm> list() {
        List<VipInfoDO> vipInfos = vipInfoDao.findByDeletedFalse();
        return vipInfos.stream().map(data -> {
            VipInfoForm res = new VipInfoForm();
            BeanUtils.copyProperties(data, res);
            return res;
        }).collect(Collectors.toList());
    }

    @Override
    public VipInfoForm getAlipayApplet() {
        List<VipInfoForm> vipInfos = this.list();
        return vipInfos.stream().filter(data -> data.getType().equals(OrganizationType.ALIPAY)).findFirst().orElse(new VipInfoForm());
    }

    @Override
    public List<VipInfoVO> getByType(OrganizationType type, Long accountId) {
        AccountDO accountInfo = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(accountInfo.getOrganizationId() != null, AccountExceptionEnum.ACCOUNT_ORG_INFO_NOT_EXIST);
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(accountInfo.getOrganizationId());
        if (organization.getOrganizationSourceId() == null) {
            organization.setOrganizationSourceId(1L);
        }
        OrganizationSourceDO organizationSource = organizationSourceDao.findInfoByIdAndDeletedFalse(organization.getOrganizationSourceId());
        if (4L == organizationSource.getId()) {
            List<VipInfoDO> vipInfos = vipInfoDao.findByTypeAndDeletedFalse(OrganizationType.PROVINCE);
            return vipInfos.stream().map(data -> {
                VipInfoVO res = new VipInfoVO();
                BeanUtils.copyProperties(data, res);
                res.setIsDefault(data.getDefaulted());
                res.setIsRecommend(data.getRecommended());
                return res;
            }).collect(Collectors.toList());
        }
        Double discount = organizationSource.getDiscount();
        List<VipInfoDO> vipInfos = vipInfoDao.findByTypeAndDeletedFalse(type);
        return vipInfos.stream().map(data -> {
            VipInfoVO res = new VipInfoVO();
            BeanUtils.copyProperties(data, res);
            Double totalPrice = res.getTotalPrice();
            res.setDiscount(discount == 1.0 ? null : discount);
            if (!(res.getId() == 8L || res.getId() == 13L)) {
                double discountPrice = (int) (totalPrice * discount);
                res.setDiscountPrice(discountPrice == totalPrice ? null : discountPrice);
            }
            res.setIsDefault(data.getDefaulted());
            res.setIsRecommend(data.getRecommended());
            return res;
        }).collect(Collectors.toList());
    }

    @Override
    public Boolean verifyBuy(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        if (ObjectUtils.isEmpty(account.getOrganizationId())) {
            return false;
        }
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(account.getOrganizationId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
        OrgVipRecordDO orgVipRecord = orgVipRecordDao.findRecordByOrganizationIdAndDeletedFalse(organization.getId());
        return !ObjectUtils.isEmpty(orgVipRecord);
    }

    @Override
    public AccountVipInfoVO getPerson(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        Integer searchTimesApplet = agentService.getSearchTimesApplet(accountId);

        Integer usedTimesApplet = agentService.getUsedTimesApplet(accountId);

        AccountVipInfoVO res = new AccountVipInfoVO();
        res.setTotalUseNum(usedTimesApplet);
        res.setUseNum(usedTimesApplet);
        res.setTotalNum(searchTimesApplet);
        res.setExpiresTime(new Timestamp(2006231634000L));
        return res;
    }

    @Override
    public PageVO<OrgVipVO> getOrganization(SearchEnterVipFrom form, Pageable pageable) {
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(form.getId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
        Page<IVipOrderDao.OrgVipInfo> orgVipInfo = vipOrderDao.getOrgVipInfo(form.getId(), pageable);
        return new PageVO<>(orgVipInfo.getTotalElements(), orgVipInfo.getContent().stream()
                .map(data -> {
                    OrgVipVO res = new OrgVipVO();
                    BeanUtils.copyProperties(data, res);
                    return res;
                }).collect(Collectors.toList()));
    }

    @Override
    public OrgRecordVO getRecord(SearchEnterVipFrom form) {
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(form.getId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
        OrgRecordVO res = new OrgRecordVO();
        OrgVipRecordDO orgVipRecord = orgVipRecordDao.findRecordByOrganizationIdAndDeletedFalse(organization.getId());

        if (ObjectUtils.isEmpty(orgVipRecord)) {
            return res;
        }
        res.setTotalNum(orgVipRecord.getTotalSearchTimes());

        List<AccountDO> orgAccounts = accountDao.findAccountByOrganizationIdAndDeletedFalse(organization.getId());
        if (!CollectionUtils.isEmpty(orgAccounts)) {
            List<QueryRecordDO> queryRecords = queryRecordDao.findByCreatorIdInAndAuthorizedResultAndDeletedFalse(
                    orgAccounts.stream().map(AccountDO::getId).collect(Collectors.toList()), AuthResultType.AUTHORIZED
            );
            if (!CollectionUtils.isEmpty(queryRecords)) {
                res.setTotalUseNum(queryRecords.size());
                res.setCheckPersonUseNum((int) queryRecords.stream().filter(data -> SearchType.PERSON.equals(data.getType())).count());
                res.setCheckEnterUseNum((int) queryRecords.stream().filter(data -> SearchType.ENTERPRISE.equals(data.getType())).count());
            }
        }
        return res;
    }

    @Override
    public PageVO<OrgRecordListVO> getRecordList(SearchEnterVipRecordFrom form, Pageable pageable) {
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(form.getId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);

        List<AccountDO> orgAccounts = accountDao.findAccountByOrganizationIdAndDeletedFalse(organization.getId());
        if (CollectionUtils.isEmpty(orgAccounts)) {
            return new PageVO<>(0L, Collections.emptyList());
        }
        Page<QueryRecordDO> queryRecords = queryRecordDao.findByCreatorIdInAndAuthorizedResultAndTypeAndDeletedFalse(
                orgAccounts.stream().map(AccountDO::getId).collect(Collectors.toList()), AuthResultType.AUTHORIZED,
                SearchType.getValue(form.getType()), pageable
        );

        return new PageVO<>(queryRecords.getTotalElements(), queryRecords.getContent().stream().map(
                data -> {
                    OrgRecordListVO res = new OrgRecordListVO();
                    SearchRecordContentDTO searchRecord = JSON.parseObject(data.getContent(), SearchRecordContentDTO.class);
                    res.setName(searchRecord.getName());
                    res.setType(data.getType());
                    res.setCreateTime(data.getCreateTime());
                    return res;
                }
        ).collect(Collectors.toList()));
    }

    @Override
    public Boolean addRecord(AddVipRecordForm form) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(form.getAccountId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(account.getOrganizationId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
        OrgVipRecordDO orgVipRecord = orgVipRecordDao.findRecordByOrganizationIdAndDeletedFalse(organization.getId());
        AssertUtil.assertTrue(ObjectUtils.isEmpty(orgVipRecord), VipExceptionEnum.VIP_RECORD_EXIST);

        OrgVipRecordDO res = new OrgVipRecordDO();
        res.setOrganizationId(organization.getId());
        res.setTotalSearchTimes(form.getTotalSearchNum());
        res.setAccountNum(form.getAccountNum());
        orgVipRecordDao.save(res);
        return true;
    }

    @Override
    public Boolean modifyRecord(ModifyVipRecordForm form) {
        OrgVipRecordDO vipRecord = orgVipRecordDao.findRecordByIdAndDeletedFalse(form.getId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(vipRecord), VipExceptionEnum.VIP_RECORD_NOT_EXIST);
        if (!vipRecord.getTotalSearchTimes().equals(form.getTotalSearchNum())
                || !vipRecord.getAccountNum().equals(form.getAccountNum())) {
            OrgVipRecordDO res = new OrgVipRecordDO();
            BeanUtils.copyProperties(vipRecord, res);
            res.setId(null);
            res.setTotalSearchTimes(form.getTotalSearchNum());
            res.setAccountNum(form.getAccountNum());
            orgVipRecordDao.save(res);
            vipRecord.setDeleted(true);
            orgVipRecordDao.save(vipRecord);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteRecord(Long accountId, List<Long> id) {
        orgVipRecordDao.deleteByIdIn(id, accountId, new Timestamp(System.currentTimeMillis()));
        return true;
    }

    @Override
    public PageVO<VipRecordVO> listRecord(SearchAccountForm form, Pageable pageable) {
        List<IAccountDao.AccountUserInfo> accountUserInfos = accountDao.searchAccountInfo(form.getPhone(), Arrays.asList(UserType.PRIMARY, UserType.SUB));
        if (CollectionUtils.isEmpty(accountUserInfos)) {
            return new PageVO<>(0L, Collections.emptyList());
        }

        List<Long> organizationIds = accountUserInfos.stream().map(IAccountDao.AccountUserInfo::getOrganizationId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if (CollectionUtils.isEmpty(organizationIds)) {
            return new PageVO<>(0L, Collections.emptyList());
        }
        Map<Long, OrganizationDO> organizationMap = organizationDao.findByIdInAndDeletedFalse(organizationIds).stream().collect(Collectors.toMap(OrganizationDO::getId, d -> d));

        Page<OrgVipRecordDO> page = orgVipRecordDao.findByOrganizationIdInAndDeletedFalse(organizationIds, pageable);

        return new PageVO<>(page.getTotalElements(), page.getContent().stream().map(data -> {
            VipRecordVO res = new VipRecordVO();
            BeanUtils.copyProperties(data, res);
            res.setTotalSearchNum(data.getTotalSearchTimes());
            if (organizationMap.containsKey(data.getOrganizationId())) {
                OrganizationDO organization = organizationMap.get(data.getOrganizationId());
                res.setType(organization.getType());
                res.setName(OrganizationType.ENTER.equals(organization.getType()) ? organization.getEnterName() : organization.getUsername());
            }
            return res;
        }).collect(Collectors.toList()));
    }

    @Override
    public void addFreeSearchTimes(InviteCodeDO codeInfo, Long organizationId) {
        if (codeInfo != null && InviteCodeType.INVITE.equals(codeInfo.getType()) && codeInfo.getVipUseTimes() != null && codeInfo.getVipUseTimes() > 0) {
            OrgVipRecordDO orgVipRecord = orgVipRecordDao.findRecordByOrganizationIdAndDeletedFalse(organizationId);
            if (orgVipRecord == null) {
                orgVipRecord = new OrgVipRecordDO();
                orgVipRecord.setOrganizationId(organizationId);
                orgVipRecord.setTotalSearchTimes(codeInfo.getVipUseTimes());
                orgVipRecord.setFreeSearchTimes(codeInfo.getVipUseTimes());
                orgVipRecordDao.save(orgVipRecord);
            }
        }
    }

}
